package com.dcits.business.server.jvm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.apache.log4j.Logger;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.dcits.business.server.dubbo.DubboExtraParameter;
import com.dcits.business.server.dubbo.DubboServer;
import com.dcits.business.server.linux.LinuxExtraParameter;
import com.dcits.business.server.linux.LinuxServer;
import com.dcits.business.server.tomcat.TomcatExtraParameter;
import com.dcits.business.server.tomcat.TomcatServer;
import com.dcits.business.server.weblogic.WeblogicExtraParameter;
import com.dcits.business.server.weblogic.WeblogicServer;
import com.dcits.tool.StringUtils;
import com.dcits.tool.ssh.SSHUtil;

public class JvmServer extends LinuxServer {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	public static final String SERVER_TYPE_NAME = "jvm";
	
	private static final Logger logger = Logger.getLogger(JvmServer.class);
	
	private static final Map<String, Object> alertThreshold = new HashMap<String, Object>();
	
	static {
		// TODO Auto-generated method stub
		alertThreshold.put("edenSpacePercent", 100);
		alertThreshold.put("oldSpacePercent", 100);
		alertThreshold.put("permSpacePercent", 100);
		alertThreshold.put("blockedThreadCount", 10);
	}
	
	private Integer dubboPort;
	private Integer weblogicPort;
	private Integer tomcatPort;
	private String javaHome;
	private Integer pid;
	private String processName;
	//是否为关联监控
	private Boolean relevanced = true;
	
	public JvmServer() {
		// TODO Auto-generated constructor stub
		super(new JvmMonitoringInfo());
	}

	@Override
	public void getMonitoringInfo() {
		// TODO Auto-generated method stub
		String str = null;
		String blockedThreadCount = "0";
		try {
			str = SSHUtil.execCommand(this.getConn(), this.javaHome + "/bin/jstat -gcutil " + this.pid, 5, 0, "");	
			blockedThreadCount = SSHUtil.execCommand(this.getConn(), this.javaHome + "/bin/jstack " + this.pid + "|grep BLOCKED|wc -l", 1, 0, "");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			//e.printStackTrace();
			logger.error("获取jvm信息出错!", e);
			this.setConnectStatus("获取jvm信息出错  " + (e.getMessage() == null ? "" : e.getMessage()));
		}
		((JvmMonitoringInfo) this.info).setBlockedThreadCount(blockedThreadCount.trim());	
		parseInfo(str);				
	}
	
	/**
	 * 解析jstat返回的jvm信息
	 * @param str
	 */
	public void parseInfo(String str) {
		if (StringUtils.isNotEmpty(str)) {
			//  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
			//  0.00  35.77  34.13  74.47  99.73   1971   32.001    25    7.382   39.383
			str = str.trim();
			String[] infos = ((str.split("\\n")[1]).trim()).split("\\s+");
			JvmMonitoringInfo realTimeInfo = (JvmMonitoringInfo) this.info;
			if (infos.length == 10 || infos.length == 11) { //不同版本的jdk打印的内容不一样， JDK1.8  11列  JDK1.7 10列 M替代P
				realTimeInfo.setSurvivorSpacePercent_0(infos[0]);
				realTimeInfo.setSurvivorSpacePercent_1(infos[1]);
				realTimeInfo.setEdenSpacePercent(infos[2]);
				realTimeInfo.setOldSpacePercent(infos[3]);
				
				realTimeInfo.setPermSpacePercent(infos[4]); //JDK1.8为Metaspace  JDK1.7为PermGen
				
				int m = infos.length == 10 ? 4 : 5;
				
				realTimeInfo.setYoungGCTotalCount(infos[++m]);
				realTimeInfo.setYoungGCTime(infos[++m]);
				realTimeInfo.setFullGCTotalCount(infos[++m]);
				realTimeInfo.setFullGCTime(infos[++m]);
				realTimeInfo.setGcTotalTime(infos[++m]);
				realTimeInfo.setTime(new Date());
				
				this.setConnectStatus("true");
				return;
			} 
		}
		this.setConnectStatus("没有获取到该进程的jvm信息,请检查进程是否还存在[pid=" + this.pid + "]并尝试重新连接!");
	}
	
	/**
	 * 添加正在监控的weblogic的jvm信息
	 * @param weblogic
	 * @return
	 * @throws Exception 
	 */
	public static JvmServer addJvm(DubboServer dubbo) throws Exception {
		//检查是否有完整的附加参数
		if (StringUtils.isEmpty(dubbo.getParameters())) {
			throw new Exception("附加参数没有设置,请检查!");
		}
		
		//获取附加参数
		DubboExtraParameter parameter = null;
		try {
			parameter = JSONObject.parseObject(dubbo.getParameters(), new TypeReference<DubboExtraParameter>(){});
		} catch (Exception e) {
			// TODO: handle exception
			logger.error("dubbo附加参数解析错误：" + dubbo.getParameters(), e);
			throw new Exception("dubbo附加参数解析错误,请检查!");
		}
		
		JvmServer jvmServer = new JvmServer();
		jvmServer.setHost(dubbo.getHost());
		jvmServer.setRealHost(dubbo.getRealHost());
		jvmServer.setPort(22);
		jvmServer.setId(dubbo.getId());
		jvmServer.setTomcatPort(dubbo.getPort());
		jvmServer.setUsername(parameter.getLinuxLoginUsername());
		jvmServer.setPassword(parameter.getLinuxLoginPassword());
		jvmServer.setJavaHome(parameter.getJavaHome());
		jvmServer.setTags(dubbo.getTags());
		
		jvmServer.setProcessPid(jvmServer.getDubboPort());
		jvmServer.execJstat();
		jvmServer.getMonitoringInfo();
		jvmServer.setServerType(JvmServer.SERVER_TYPE_NAME);
		jvmServer.setProcessName("dubbo(" + dubbo.getHost() + ":" + dubbo.getPort()+ ")");
				
		return jvmServer;
	}
	
	/**
	 * 添加正在监控的weblogic的jvm信息
	 * @param weblogic
	 * @return
	 * @throws Exception 
	 */
	public static JvmServer addJvm(WeblogicServer weblogic) throws Exception {
		//检查是否有完整的附加参数
		if (StringUtils.isEmpty(weblogic.getParameters())) {
			throw new Exception("附加参数没有设置,请检查!");
		}
		
		//获取附加参数
		WeblogicExtraParameter parameter = null;
		try {
			parameter = JSONObject.parseObject(weblogic.getParameters(), new TypeReference<WeblogicExtraParameter>(){});
		} catch (Exception e) {
			// TODO: handle exception
			logger.error("weblogic附加参数解析错误：" + weblogic.getParameters(), e);
			throw new Exception("weblogic附加参数解析错误,请检查!");
		}
		
		JvmServer jvmServer = new JvmServer();
		jvmServer.setHost(weblogic.getHost());
		jvmServer.setRealHost(weblogic.getRealHost());
		jvmServer.setPort(22);
		jvmServer.setId(weblogic.getId());
		jvmServer.setWeblogicPort(weblogic.getPort());
		jvmServer.setUsername(parameter.getLinuxLoginUsername());
		jvmServer.setPassword(parameter.getLinuxLoginPassword());
		jvmServer.setJavaHome(parameter.getJavaHome());
		jvmServer.setTags(weblogic.getTags());
		
		jvmServer.setProcessPid(jvmServer.getWeblogicPort());
		jvmServer.execJstat();
		jvmServer.getMonitoringInfo();
		jvmServer.setServerType(JvmServer.SERVER_TYPE_NAME);
		jvmServer.setProcessName("weblogic(" + weblogic.getHost() + ":" + weblogic.getPort() + ")");
				
		return jvmServer;
	}
	
	/**
	 * 添加正在监控的tomcat的jvm信息
	 * @param weblogic
	 * @return
	 * @throws Exception 
	 */
	public static JvmServer addJvm(TomcatServer tomcat) throws Exception {
		//检查是否有完整的附加参数
		if (StringUtils.isEmpty(tomcat.getParameters())) {
			throw new Exception("附加参数没有设置,请检查!");
		}
		
		//获取附加参数
		TomcatExtraParameter parameter = null;
		try {
			parameter = JSONObject.parseObject(tomcat.getParameters(), new TypeReference<TomcatExtraParameter>(){});
		} catch (Exception e) {
			// TODO: handle exception
			logger.error("weblogic附加参数解析错误：" + tomcat.getParameters(), e);
			throw new Exception("weblogic附加参数解析错误,请检查!");
		}
		
		JvmServer jvmServer = new JvmServer();
		jvmServer.setHost(tomcat.getHost());
		jvmServer.setRealHost(tomcat.getRealHost());
		jvmServer.setPort(22);
		jvmServer.setId(tomcat.getId());
		jvmServer.setTomcatPort(parameter.getWebPort());
		jvmServer.setUsername(parameter.getLinuxLoginUsername());
		jvmServer.setPassword(parameter.getLinuxLoginPassword());
		jvmServer.setJavaHome(parameter.getJavaHome());
		jvmServer.setTags(tomcat.getTags());
		
		jvmServer.setProcessPid(jvmServer.getTomcatPort());
		jvmServer.execJstat();
		jvmServer.getMonitoringInfo();
		jvmServer.setServerType(JvmServer.SERVER_TYPE_NAME);
		jvmServer.setProcessName("tomcat(" + tomcat.getHost() + ":" + parameter.getWebPort() + ")");
				
		return jvmServer;
	}
	
	/**
	 * 添加正在监控的linux服务器上的jvm信息
	 * @param linux
	 * @param pid
	 * @param processName
	 * @return
	 */
	public static JvmServer addJvm(LinuxServer linux, Integer pid, String processName) throws Exception  {
		//检查是否有完整的附加参数
		if (StringUtils.isEmpty(linux.getParameters())) {
			throw new Exception("附加参数没有设置,请检查!");
		}
		//获取附加参数
		LinuxExtraParameter parameter = null;
		try {
			parameter = JSONObject.parseObject(linux.getParameters(), new TypeReference<LinuxExtraParameter>(){});
		} catch (Exception e) {
			// TODO: handle exception
			logger.error("linux附加参数解析错误：" + linux.getParameters(), e);
			throw new Exception("linux附加参数解析错误,请检查!");
		}
		
		if (StringUtils.isEmpty(processName)) processName = linux.getHost();
		
		JvmServer jvmServer = new JvmServer();

		jvmServer.setHost(linux.getHost());
		jvmServer.setRealHost(linux.getRealHost());
		jvmServer.setPort(linux.getPort());
		jvmServer.setId(linux.getId());

		jvmServer.setUsername(linux.getUsername());
		jvmServer.setPassword(linux.getPassword());
		jvmServer.setJavaHome(parameter.getJavaHome());
		jvmServer.setTags(processName + "-" + pid);
		jvmServer.setPid(pid);
		jvmServer.setProcessName(processName);

		jvmServer.execJstat();
		jvmServer.getMonitoringInfo();		
		jvmServer.setServerType(JvmServer.SERVER_TYPE_NAME);
		
		jvmServer.setRelevanced(false);
		return jvmServer;
	}
	
	/**
	 * 获取weblogic的pid信息
	 * @throws Exception 
	 */
	public void setProcessPid(Integer port) throws Exception {
/*				
		if (StringUtils.isEmpty(String.valueOf(this.weblogicPort))) {
			throw new Exception("请先删除然后手动创建连接!");
		}*/
		String flag = this.connect();
		if (!"true".equals(flag)) {
			throw new Exception(flag);
		}
		
		String execCommand = "netstat -anp|grep java|grep LISTEN|grep " + port + "|head -1";
		String pidStr = null;
		try {
			pidStr = SSHUtil.execCommand(this.getConn(), execCommand, 1, 0, "");			
		} catch (Exception e) {
			// TODO: handle exception
			e.printStackTrace();
			throw new Exception("获取进程对应的pid信息时发生了错误:" + e.getMessage());
		}
		
		if (StringUtils.isEmpty(pidStr)) {
			throw new Exception("没有获取到该进程对应的pid进程信息!");
		}
		String[] strs = pidStr.trim().split("(\\s)+");
		pidStr = (strs[strs.length - 1]);
		this.pid = Integer.valueOf(pidStr.substring(0, pidStr.indexOf("/")));
	}
	
	
	public void execJstat() throws Exception {
		
		if (this.getConn() == null) {
			String flag = this.connect();
			if (!"true".equals(flag)) {
				throw new Exception(flag);
			}
		}

		//检测命令是否可用或者pid的java进程是否存在
		String returnStr = SSHUtil.execCommand(this.getConn(), this.javaHome + "/bin/jstat -gcutil " + this.pid, 1, 0, "");
		if (StringUtils.isEmpty(returnStr)) {
			throw new Exception("执行jstat命令发生错误：请检查JAVA_HOME、主机环境以及PID是否正确.[execCommand=" + this.javaHome + "/bin/jstat -gcutil " + this.pid + "]");	
		}	
	}
	
	public void setWeblogicPort(Integer weblogicPort) {
		this.weblogicPort = weblogicPort;
	}
	
	public Integer getWeblogicPort() {
		return weblogicPort;
	}
	
	public void setJavaHome(String javaHome) {
		this.javaHome = javaHome;
	}
	
	public String getJavaHome() {
		return javaHome;
	}
	
	public void setPid(Integer pid) {
		this.pid = pid;
	}
	
	public Integer getPid() {
		return pid;
	}
	
	public void setProcessName(String processName) {
		this.processName = processName;
	}
	
	public String getProcessName() {
		return processName;
	}
	
	public void setTomcatPort(Integer tomcatPort) {
		this.tomcatPort = tomcatPort;
	}
	
	public Integer getTomcatPort() {
		return tomcatPort;
	}
	
	public Boolean getRelevanced() {
		return relevanced;
	}
	
	public void setRelevanced(Boolean relevanced) {
		this.relevanced = relevanced;
	}
	
	public void setDubboPort(Integer dubboPort) {
		this.dubboPort = dubboPort;
	}
	
	public Integer getDubboPort() {
		return dubboPort;
	}
}
